/*******************************************************************************
 * Copyright (c) 2014 Francesco Gabbrielli (http://www.francescogabbrielli.it).
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Francesco Gabbrielli - initial API and implementation
 ******************************************************************************/
package it.francescogabbrielli.android.solidalapp;

import it.francescogabbrielli.android.solidalapp.EMF;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.response.CollectionResponse;
import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.datanucleus.query.JPACursorHelper;

import java.util.List;
import java.util.logging.Logger;

import javax.annotation.Nullable;
import javax.inject.Named;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityNotFoundException;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

@Api(name = "profileendpoint", namespace = @ApiNamespace(ownerDomain = "francescogabbrielli.it", ownerName = "francescogabbrielli.it", packagePath = "android.solidalapp"))
public class ProfileEndpoint {
	
	private static final Logger logger = Logger.getLogger(ProfileEndpoint.class.getName());

	/**
	 * This method lists all the entities inserted in datastore.
	 * It uses HTTP GET method and paging support.
	 *
	 * @return A CollectionResponse class containing the list of all entities
	 * persisted and a cursor to the next page.
	 */
	@SuppressWarnings({ "unchecked", "unused" })
	@ApiMethod(name = "listProfile")
	public CollectionResponse<Profile> listProfile(
			@Nullable @Named("cursor") String cursorString,
			@Nullable @Named("limit") Integer limit) {

		EntityManager mgr = null;
		Cursor cursor = null;
		List<Profile> execute = null;

		try {
			mgr = getEntityManager();
			Query query = mgr.createQuery("select from Profile as Profile");
			if (cursorString != null && cursorString != "") {
				cursor = Cursor.fromWebSafeString(cursorString);
				query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
			}

			if (limit != null) {
				query.setFirstResult(0);
				query.setMaxResults(limit);
			}

			execute = (List<Profile>) query.getResultList();
			cursor = JPACursorHelper.getCursor(execute);
			if (cursor != null)
				cursorString = cursor.toWebSafeString();

			// Tight loop for fetching all entities from datastore and accomodate
			// for lazy fetch.
			for (Profile obj : execute)
				;
		} finally {
			mgr.close();
		}

		return CollectionResponse.<Profile> builder().setItems(execute)
				.setNextPageToken(cursorString).build();
	}

	/**
	 * This method gets the Profile by google account identifier. 
	 * It uses HTTP GET method.
	 *
	 * @param googleId the google account id
	 * @return The profile entity
	 */
	@ApiMethod(name = "getProfileByAccount", path="account")
	public Profile getProfileByAccount(@Named("googleId") String googleId) {
		
		EntityManager mgr = getEntityManager();
		Profile profile = null;
		
		try {
			profile = findProfile(mgr, googleId);			
		} catch(Exception e) {
			logger.severe("Error reading profile: "+googleId);
		} finally {
			mgr.close();
		}
		
		logger.info("Profile: "+googleId);
		return profile;
		
	}
	
	private Profile findProfile(EntityManager mgr, String googleId) throws Exception {
		Profile profile = null;
		Query query = mgr.createQuery(
				"select from Profile as Profile where googleId = :googleId");
		query.setParameter("googleId", googleId);
		
		@SuppressWarnings({"unchecked"})
		List<Profile> r = (List<Profile>) query.getResultList();
		if (!r.isEmpty())
			profile = r.get(0);
		return profile;
	}
	
	/**
	 * This method gets the profile having primary key id. It uses HTTP GET method.
	 *
	 * @param id the primary key of profile.
	 * @return The profile entity with primary key id.
	 */
	@ApiMethod(name = "getProfile")
	public Profile getProfile(@Named("id") Long id) {
		EntityManager mgr = getEntityManager();
		Profile profile = null;
		try {
			profile = mgr.find(Profile.class, id);
		} finally {
			mgr.close();
		}
		return profile;
	}
	

	/**
	 * This inserts a new entity into App Engine datastore. If the entity already
	 * exists in the datastore, an exception is thrown.
	 * It uses HTTP POST method.
	 *
	 * @param profile the entity to be inserted.
	 * @return The inserted entity.
	 */
	@ApiMethod(name = "insertProfile")
	public Profile insertProfile(Profile profile) {
		EntityManager mgr = getEntityManager();
		EntityTransaction tx = mgr.getTransaction();
		try {
			tx.begin();
			if (profile.getProfileId()!=null && containsProfile(profile))
				throw new EntityExistsException("Profile already exists? ID="+profile.getProfileId());
			Profile check = findProfile(mgr, profile.getGoogleId());
			if (check!=null)
				throw new EntityExistsException("Profile already exists? account="+profile.getGoogleId());
			mgr.persist(profile);
			tx.commit();
		} catch(Exception e) {
			logger.severe("Error reading profile for update: "+e);
		} finally {
			if (tx.isActive())
				tx.rollback();
			mgr.close();
		}
		return profile;
	}

	/**
	 * This method is used for updating an existing entity. If the entity does not
	 * exist in the datastore, an exception is thrown.
	 * It uses HTTP PUT method.
	 *
	 * @param profile the entity to be updated.
	 * @return The updated entity.
	 */
	@ApiMethod(name = "updateProfile")
	public Profile updateProfile(Profile profile) {
		EntityManager mgr = getEntityManager();
		try {
			if (!containsProfile(profile)) {
				throw new EntityNotFoundException("Profile does not exist; ID="+profile.getProfileId());
			}
			mgr.persist(profile);
		} finally {
			mgr.close();
		}
		return profile;
	}

	/**
	 * This method removes the entity with primary key id.
	 * It uses HTTP DELETE method.
	 *
	 * @param id the primary key of the entity to be deleted.
	 */
	@ApiMethod(name = "removeProfile")
	public void removeProfile(@Named("id") String id) {
		EntityManager mgr = getEntityManager();
		try {
			Profile profile = mgr.find(Profile.class, id);
			mgr.remove(profile);
		} finally {
			mgr.close();
		}
	}

	private boolean containsProfile(Profile profile) {
		EntityManager mgr = getEntityManager();
		boolean contains = true;
		try {
			Profile item = mgr.find(Profile.class, profile.getProfileId());
			if (item == null) {
				contains = false;
			}
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			mgr.close();
		}
		return contains;
	}

	private static EntityManager getEntityManager() {
		return EMF.get().createEntityManager();
	}

}
